#pragma once

#include <filesystem>
#include <opencv2/opencv.hpp>

class ImageLoader {
public:
	virtual bool FetchImage() = 0;
	virtual ~ImageLoader() {}

	int width() { return width_; }
	int height() { return height_; }
	int frame_index() { return frame_index_; }
	cv::Mat image() { return image_; }

protected:
	int frame_index_ = 0;
	cv::Mat image_;
	int width_ = 0, height_ = 0;
};

class LiveLoader: public ImageLoader {
public:
	static LiveLoader* Build(int cam_id);
	bool FetchImage() override;

private:
	cv::VideoCapture cap_;
};

class VideoLoader: public ImageLoader {
public:
	VideoLoader(const std::string& frames);
	bool FetchImage() override;

private:
	cv::VideoCapture cap_;
};

class FolderImageLoader : public ImageLoader {
public:
	static FolderImageLoader* Build(
		const std::filesystem::path& image_dir,
		const std::string& ext);

	bool FetchImage() override;

protected:
	std::vector<std::string> files_;
	int frame_count_;
};

class BufferImageLoader : public ImageLoader {
public:
	BufferImageLoader(
		std::shared_ptr<std::vector<unsigned char>> buf,
		int width, int height)
		: buf_ptr(buf) 
	{
		width_ = width;
		height_ = height;
	}

	bool FetchImage() override {
		image_ = cv::Mat(height(), width(), CV_8UC3, buf_ptr->data());
		return true;
	}

protected:
	std::shared_ptr<std::vector<unsigned char>> buf_ptr;
};

class ImageWriter {
public:
	virtual bool WriteImage(const cv::Mat& image) = 0;
	virtual ~ImageWriter() {}

	int width() { return width_; }
	int height() { return height_; }

protected:
	int width_ = 0, height_ = 0;
};

class VideoImageWriter : public ImageWriter {
public:
	static VideoImageWriter* Build(
		const std::filesystem::path& video_path, 
		int width, int height);

	bool WriteImage(const cv::Mat& image) override;

	cv::VideoWriter writer_;
	
};